package com.mytest.test.until;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.springframework.util.StringUtils;

/**
 * mybatis-plus自定义sql模板，自定义批量更新或插入
 * 定义类似mapper.xml模板
 * chengxucai 20211123
 * @author chengxucai
 */
public class MysqlInsertOrUpdateBath extends AbstractMethod {
    /**
     * 注入自定义 MappedStatement
     */
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        //带上ON DUPLICATE KEY UPDATE 子句，而要插入的行与表中现有记录的惟一索引或主键中产生重复值，那么就会发生旧行的更新；如果插入的行数据与现有表中记录的唯一索引或者主键不重复，则执行新纪录插入操作
                /*<script>insert into students (class_no,student_no,student_name,home_address,update_time) values
                <foreach collection="list" item="item" index="index" open="(" separator="),(" close=")">
                #{item.classNo},#{item.studentNo},#{item.studentName},#{item.homeAddress},#{item.updateTime}
                </foreach>
                ON DUPLICATE KEY UPDATE
                class_no=(class_no),student_no=VALUES(student_no),student_name=VALUES(student_name),home_address=VALUES(home_address),update_time=VALUES(update_time)
                </script>*/
        //1.语句模板
        final String sql = "<script>insert into %s %s values %s ON DUPLICATE KEY UPDATE %s</script>";
        //2.获取表名
        final String tableName = tableInfo.getTableName();
        //3.准备属性名
        final String filedSql = prepareFieldSql(tableInfo);
        //4.遍历语句
        //item = “” ，集合里面的单个值，给下面  #{ } 用
        //index = "" ，这个是遍历的下标
        //open  separator  close  这3个比较好理解，就是  （ ， ， ，） 这样子啦，拼接括号，中间逗号隔开的意思
        final String modelValuesSql = prepareModelValuesSql(tableInfo);
        //5.准备ON DUPLICATE KEY UPDATE sql
        final String duplicateKeySql =prepareDuplicateKeySql(tableInfo);
        //6.拼接
        final String sqlResult = String.format(sql, tableName, filedSql, modelValuesSql,duplicateKeySql);
        //7.创建SqlSource
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        // 添加 MappedStatement 到 Mybatis 容器,第三个参数必须和baseMapper的自定义方法名一致
        return this.addInsertMappedStatement(mapperClass, modelClass, "mysqlInsertOrUpdateBath", sqlSource, new NoKeyGenerator(), null, null);
    }

    /**
     * 获取表所有的字段
     * @param tableInfo
     * @return
     */
    private String prepareFieldSql(TableInfo tableInfo) {
        StringBuilder fieldSql = new StringBuilder();
        fieldSql.append(tableInfo.getKeyColumn()).append(",");
        tableInfo.getFieldList().forEach(x -> {
            fieldSql.append(x.getColumn()).append(",");
        });
        fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
        fieldSql.insert(0, "(");
        fieldSql.append(")");
        return fieldSql.toString();
    }

    /**
     * 准备ON DUPLICATE KEY UPDATE sql
     * 即ON DUPLICATE KEY UPDATE 的sql
     * @param tableInfo
     * @return
     */
    private String prepareDuplicateKeySql(TableInfo tableInfo) {
        final StringBuilder duplicateKeySql = new StringBuilder();
        if(!StringUtils.isEmpty(tableInfo.getKeyColumn())) {
            duplicateKeySql.append(tableInfo.getKeyColumn()).append("=(").append(tableInfo.getKeyColumn()).append("),");
        }

        tableInfo.getFieldList().forEach(x -> {
            duplicateKeySql.append(x.getColumn())
                    .append("=VALUES(")
                    .append(x.getColumn())
                    .append("),");
        });
        duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
        return duplicateKeySql.toString();
    }

    private String prepareModelValuesSql(TableInfo tableInfo){
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        if(!StringUtils.isEmpty(tableInfo.getKeyProperty())) {
            valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
        }
        tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
        valueSql.delete(valueSql.length() - 1, valueSql.length());
        valueSql.append("</foreach>");
        return valueSql.toString();
    }
}

